#include "grayimage.h"
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>

using namespace std;

void GrayImage::del()
{
    if (_pixels)
        delete [] _pixels;
    _pixels=NULL;
    _Rows =0;
    _Cols=0;

}

void GrayImage::nw(const unsigned int parRows, const unsigned int parCols)
{
    if ((parRows)&&(parCols))
    {
        _pixels= new unsigned char[parRows*parCols];
        _Rows=parRows;
        _Cols=parCols;
        for(unsigned int x=0; x< parRows*parCols; x++)
            *(_pixels+x)=0;
    }
    else
    {
        _pixels= NULL;
        _Rows=0;
        _Cols=0;
    }
}

GrayImage::GrayImage():RectSupport(0, 0)
{
    _pixels = NULL;
}

GrayImage::GrayImage(const unsigned int parRows, const unsigned int parCols):RectSupport(parRows, parCols)
{
    if (parRows && parCols)
        nw(parRows, parCols);
    else
    {
        _pixels = NULL;
        _Rows = 0;
        _Cols = 0;
    }
}

GrayImage::GrayImage(GrayImage& parGrayImage):RectSupport(parGrayImage._Rows, parGrayImage._Cols)
{
    if (parGrayImage._Rows && parGrayImage._Cols)
    {
        nw(parGrayImage._Rows,parGrayImage._Cols);
        for (unsigned int x=0; x<_Rows * _Cols; x++)
            *(_pixels+x)=*(parGrayImage._pixels + x);
    }
    else
    {
        _pixels = NULL;
        _Rows = 0;
        _Cols = 0;
    }
}

GrayImage::~GrayImage()
{
    del();
}

GrayImage& GrayImage::operator =(const GrayImage& parGrayImage)
{
    if ((_Rows!=parGrayImage._Rows)||(_Cols!=parGrayImage._Cols))
    {
        del();
        nw(parGrayImage._Rows,parGrayImage._Cols);
    }
    for (unsigned int x=0; x < _Rows*_Cols ; x++)
    {
        *(_pixels+x)=*(parGrayImage._pixels+x);
    }
    return *this;
}

unsigned char GrayImage::getPixel(const unsigned int x, const unsigned int y) const
{
    if ((x<=_Rows-1)&&(y<=_Cols-1))
        return *(_pixels + x * _Cols + y);
    else
    {
        cerr << "GrayImage::getPixel - Pixel (" << x << ", " << y << ") está fora do suporte geométrico da imagem." << endl;
        exit(0);
    }
}


void GrayImage::setPixel(unsigned int x, unsigned int y, unsigned char parValue)
{
    if ((x<=_Rows-1)&&(y<=_Cols-1))
        *(_pixels+ x*_Cols +y) = parValue;
    else
    {
        cerr << "GrayImage::setPixel - Pixel (" << x << ", " << y << ") está fora do suporte geométrico da imagem." << endl;
        exit(0);
    }
}
GrayImage GrayImage::cut(const unsigned int FirstRow, const unsigned int LastRow, const unsigned int FirstCol, const unsigned int LastCol)
{
    if((FirstRow>LastRow)||(FirstCol>LastCol)||(LastRow>_Rows)||(LastCol>_Cols))
    {
        cerr << "Erro detectado pelo método GraImage::cut():" << endl << "Parâmetros de entrada fora da faixa."<<endl;
        exit (1);
    }
    GrayImage result(LastRow - FirstRow +1, LastCol - FirstCol+1);
    for(unsigned int x=0; x<result._Rows; x++)
        for(unsigned int y=0; y<result._Cols; y++)
            result.setPixel(x,y,this->getPixel(x+FirstRow, y+FirstCol));
    return result;
}


void GrayImage::show() const
{
    cout << " Image " << _Rows << 'x' << _Cols << endl;
    if(_Rows&&_Cols)
    {
        for (unsigned int x=0;x<=_Rows-1;x++)
        {
           for (unsigned int y=0;y<=_Cols-1;y++)
                cout << setw(3) << (int)getPixel (x, y) << ' ';
            cout << endl;
        }
        cout << endl;
    }
}

int getPGMSubformat(string fileName)
{
    ifstream ifs;
    string line;
    ifs.open (fileName.c_str(), std::ifstream::in);
    if(ifs.good())
    {
        getline (ifs,line);
        while(line[0]!='P')
        {
            getline (ifs,line);
        }
        ifs.close();
    }
    else
    {
        cerr << "Arquivo "<< fileName<<" não pôde ser aberto com sucesso" <<endl;
        exit(0);
    }
    if ( line[1]=='2'|| line[1]=='5')
        return line[1] -'0';
    else
        return 0;
}

GrayImage loadPGM(string fileName)
{
    ifstream ifs;
    string line;
    char c;
    unsigned int FileCols, FileRows, MaxPixelValue, Pixel;

    if(getPGMSubformat(fileName) == 2 )
    {
        ifs.open (fileName.c_str(), std::ifstream::in);
        if(ifs.good())
        {
            getline(ifs, line);
            ifs.get(c);
            while(c=='#')
            {
                getline (ifs,line);
                ifs.get(c);
            }
            ifs.putback(c);
            ifs >> FileCols >> FileRows >> MaxPixelValue;
            if (MaxPixelValue>255)
            {
                cerr << "loadPGM detectou um erro."<< endl << "Valor máximo do pixel do arquivo excede 255" <<endl;
                exit(0);
            }
            else
            {
                GrayImage ReadImage(FileRows, FileCols);
                for (unsigned int x=0; x < FileRows; x++)
                    for(unsigned int y=0; y < FileCols; y++)
                    {
                        ifs >> Pixel;
                        if (ifs.good())
                            ReadImage.setPixel(x,y,255*Pixel/MaxPixelValue);
                        else
                        {
                            cerr << "loadPGM detectou um erro."<< endl << "Arquivo terminado inesperadamente." <<endl;
                            exit(0);
                        }
                    }
                return ReadImage;
            }
        }
    }
    else
    {
        cerr << "loadPGM detectou um erro."<< endl << "Arquivo não pertence ao subtipo P2." <<endl;
        exit(0);
    }

}

void savePGM(GrayImage parImage, string fileName, int format, unsigned int maxValue)
{
    ofstream imageFile;
    if (format == 2)
    {
        imageFile.open(fileName.c_str(), ios::out | ios::trunc);
        imageFile << "P2"<<endl;
        imageFile << "# "<< fileName << endl;
        imageFile << parImage.getCols()<< " "<< parImage.getRows() << endl;
        imageFile << maxValue << endl;
        for(unsigned int x=0;x<parImage.getRows();x++)
        {
            for(unsigned int y=0;y<parImage.getCols();y++)
                imageFile << setw(4) << maxValue*parImage.getPixel(x,y)/255<<' ';
            imageFile << endl;
        }
        imageFile.close();
    }
    else
    {
        cerr << "savePGM detectou um erro:"<< endl << "formato não suportado. Esta versão grava somente aruivos PGM subtipo P2." <<endl;
        exit(0);
    }
}

GrayImage blackImage(unsigned int size)
{
    GrayImage ReturnImage(size, size);
    return ReturnImage;}

GrayImage blackImage(unsigned int parRows, unsigned int parCols)
{
    GrayImage ReturnImage(parRows, parCols);
    return ReturnImage;
}
